package com.goingup.domain;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.goingup.domain.Vector.Direction;
/**
 * Basic structure to hold and process requests for service.
 * @author pcresswell
 *
 */
public class RequestQueue {
	
	private Map<Floor,DropOffRequest> dropOffRequest = new HashMap<Floor, DropOffRequest>();
	private Map<Floor, PickUpRequest> goingUpRequests = new HashMap<Floor, PickUpRequest>();
	private Map<Floor, PickUpRequest> goingDownRequests = new HashMap<Floor, PickUpRequest>();
	private List<Request> pendingRequests = new ArrayList<Request>();
	
	
	/**
	 * Add a drop off request to the queue.
	 * @param request
	 */
	public void addDropOffRequest(DropOffRequest request){
		//TODO: Prevent duplicate requests.
		dropOffRequest.put(request.getFloor(), request);
		addRequest(request);
	}
	/**
	 * Add a pickup request to the queue.
	 * @param request
	 */
	public void addPickUpRequest(PickUpRequest request){
		if (goingUpRequests.containsKey(request.getFloor()) || 
			goingDownRequests.containsKey(request.getFloor()))
			return;
		
		if (Direction.UP.equals(request.getDirection()))
			goingUpRequests.put(request.getFloor(), request);
		else
			goingDownRequests.put(request.getFloor(), request);
		
		addRequest(request);
	}
	private void addRequest(Request request){
		pendingRequests.add(request);
	}
	/**
	 * Execute all requests in the queue
	 * @param elevator
	 */
	public synchronized void executeRequests(Elevator elevator){
		while (!isEmpty())
			executeCurrentRequest(elevator);
	}
	private boolean isEmpty(){
		return (pendingRequests.isEmpty());
	}
	private void executeCurrentRequest(Elevator elevator) {
		Request request = getNextRequest(elevator);
		request.execute(elevator);
	}
	private Request getNextRequest(Elevator elevator) {
		Request request = getRequestInDirectionAfterFloor(elevator.getCurrentFloor(), elevator.getCurrentDirection());
		if (request==null)//there are no requests for pick up or drop off in the current direction
			request = pendingRequests.remove(0);
		removeFromPendingList(request);
		return request;
	}
	private void removeFromPendingList(Request currentRequest) {
		if (dropOffRequest.containsValue(currentRequest))
			dropOffRequest.remove(currentRequest.getFloor());
		else if (goingUpRequests.containsValue(currentRequest))
			goingUpRequests.remove(currentRequest.getFloor());
		else if (goingDownRequests.containsValue(currentRequest))
			goingDownRequests.remove(currentRequest.getFloor());
	}
	/**
	 * Returns a Pick up request if one exists for the requested floor.
	 * @param currentFloor
	 * @param direction
	 * @return
	 */
	public PickUpRequest getPickupRequestForFloor(Floor currentFloor, Direction direction){
		if(Direction.UP.equals(direction))
			return goingUpRequests.get(currentFloor);
		else
			return goingDownRequests.get(currentFloor);
	}
	/**
	 * Gets the next request heading in the given direction.  That is, skip all requests for the opposite
	 * direction that the elevator is currently headed in.
	 * @param currentFloor
	 * @param direction
	 * @return
	 */
	public Request getRequestInDirectionAfterFloor(Floor currentFloor, Direction direction){
		for (Request request : pendingRequests) {
			if (request.wasExecuted())
				continue;
			else if (Direction.UP.equals(direction) && request.getFloor().isAbove(currentFloor))
				return request;
			else if (Direction.DOWN.equals(direction) && request.getFloor().isBelow(currentFloor))
				return request;
		}
		return null;
	}
	public boolean hasPickupRequestForFloor(Floor floor,Direction direction) {
		return (getPickupRequestForFloor(floor, direction)!=null);
	}
	
}
